package com.zdp.filesync.monitor;

import com.zdp.filesync.notify.service.NotifyClientSyncService;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 文件监听任务
 */
@Slf4j
public class FileMonitorTask implements Runnable {

    // 是否发生了同步操作
    private boolean isSync = false;

    // 监听的文件目录
    private String path ;

    // 间隔时间（多久目录没有变化才通知客户端进行同步操作）
    private final AtomicInteger interval;
    // JDK的文件监听对象
    private WatchService watchService;

    // 控制线程的运行
    private volatile boolean isRunning = true;

    private final NotifyClientSyncService notifyClientSyncService;

    // 记录文件变化的发生时间
    private long monitorTime;

    private String commentName;


    public FileMonitorTask(int interval,String path,
                           NotifyClientSyncService notifyClientSyncService,
                           String commentName){
        this.interval = new AtomicInteger(interval);
        this.path = path;
        this.notifyClientSyncService = notifyClientSyncService;
        this.commentName = commentName;
    }

    private void closeWatchService(WatchService watchService){
        if(Objects.nonNull(watchService)){
            try {
                watchService.close();
            } catch (IOException e) {
                e.printStackTrace();
                log.warn("关闭WatchService失败，path : {}" , path);
            }
        }
    }

    @Override
    public void run() {
        registry(path);
        log.info("{} 注册监听完毕",path);
        // 监听操作
        WatchKey key;
        try{
            while(isRunning){
                key = watchService.poll();
                if(Objects.isNull(key)){
                   notifyClient();
                }else{
                    for(WatchEvent<?> event : key.pollEvents()){
                        log.info("Event kind : {} . File affected : {} .",event.kind(),event.context());
                    }
                    key.reset();
                    // 记录 目录发生变化的时间
                    monitorTime = System.currentTimeMillis();
                    isSync = true;
                }
            }
        } finally {
            closeWatchService(watchService);
            notifyClient();
        }

    }

    private void notifyClient(){
        if(!isSync){
            return ;
        }
        long curTime = System.currentTimeMillis();
        if(curTime - monitorTime >= interval.get()){
            log.info("{} 通知客户端更新",path);
            notifyClientSyncService.notifyClient(path,commentName);
            isSync = false;
        }

    }

    private void registry(String path){
        try{
            this.watchService = FileSystems.getDefault().newWatchService();
            File file = new File(path);
            doRegistry(file,watchService);
        } catch (IOException e) {
            log.warn("{} 注册文件监听失败",path,e);
            closeWatchService(watchService);
        }

    }

    private void doRegistry(File curFile , WatchService watchService){
        if(Objects.isNull(curFile) || curFile.isFile()){
            return ;
        }
        File[] files = curFile.listFiles();
        if(Objects.nonNull(files) && files.length !=0){
            for(File file : files){
                doRegistry(file,watchService);
            }
        }
        // 注册目录
        Path path = Paths.get(curFile.getPath());
        try {
            path.register(
                    watchService,
                    StandardWatchEventKinds.ENTRY_CREATE,
                    StandardWatchEventKinds.ENTRY_DELETE,
                    StandardWatchEventKinds.ENTRY_MODIFY);
        } catch (IOException e) {
            log.warn("{}注册失败",path,e);
        }

    }

    public void cancel(){
        this.isRunning = false;
    }

    public void setInterval(Integer interval){
        this.interval.set(interval);
    }

    public void setCommentName(String commentName){
        this.commentName = commentName;
    }
}
